#pragma once

#include "Sprite Management\\AnimationManager.h"
#include "..\SGD Wrappers\SGD_Declarations.h"
#include "..\SGD Wrappers\SGD_Handle.h"
#include <sstream>
#include "Entity Manager\EntityManager.h"

class TileManager;
class AIManagerState;
class ParticleManager;
class WallTriggerObject;
class BitmapFont;
class Enemy;
class Player;
class Boss;
class IGameState;
class Game
{
private:
	/*******************************************/
	// Singleton Object:
	static Game* spInstance;

	Game(void) = default;
	~Game(void) = default;

	Game(const Game&) = delete;
	Game& operator= (const Game&) = delete;


	/*******************************************/
	// Highscores helper function
	void SortHighScores();

	/*******************************************/
	// Screen Size
	float fScreenWidth = 1;
	float fScreenHeight = 1;
	bool bFullScreen = true;

	/*******************************************/
	// Input
	bool bInput = false;

	/*******************************************/
	// Audio
	char cMusicVolume = 100;
	char cSfxVolume = 100;

	char* cObjects;

	/*******************************************/
	// Audio Assets
	SGD::HAudio hEnemyAttackSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hEnemyHurtSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hPlayerAttackSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hPlayerHurtSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hPlayerJumpSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hPickupSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hSwitchSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hCheckPointSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hSpringboardSfx = SGD::INVALID_HANDLE;
	SGD::HAudio hBackgroundMus = SGD::INVALID_HANDLE;

	/*******************************************/
	// Message Callback Function
	static void MessageProc(const SGD::Message* pMsg);

	/*******************************************/
	// Current Game State
	IGameState* pCurState = nullptr;
	IGameState*	pNextState = nullptr;

	/*******************************************/
	// Game Time
	unsigned long ulGameTime = 0;
	float elapsedTime = 0.0f;	// convert to fraction of a second

	/*******************************************/
	// GamePlayState Variables
	SGD::Point checkpointLoc;
	int iBottomLevel;

	// Which profile is selected?
	int iProfile;

	// Villagers Saved booleans (First number represents the level, second represents villager number)
	bool bVillager1_1;
	bool bVillager2_1;
	bool bVillager3_1;
	bool bVillager4_1;

	// Which stages have been completed (0 = 0 stages complete, 1 = 1st stage completed, 2 = 2nd stage completed, etc.)
	unsigned int iStageComplete;
	unsigned int iCurrentLevel;

	// Boolean for whether the player has passed the checkpoint
	bool bCheckpoint;

	// Player lives counter
	int iPlayerLives = 3;

	float fTimeAttack;
	bool bTimeAttackPause;
	int iTimeAttackLevel;

	// Create Entity Manager
	EntityManager pObjects;

	// Player Pointer
	Player * pPlayer = nullptr;

	// AI State Manager
	AIManagerState* AIManager;

	// Animation Manager
	AnimationManager* AnimManager;

	// Particle Manager
	ParticleManager* particleManager;

	// Bitmap Font
	BitmapFont* bitFont;

	// Wall Trigger Objects
	std::vector<WallTriggerObject*> wallTriggerObjects;

	std::wostringstream woFilePathStream;
	
	// Tile Map
	TileManager* tileManager;

	// High scores
	std::vector<int> vecHighScores;
	bool bBeatTimeAttack = false;

public:
	/*******************************************/
	// GamePlayState Variable Accessors/Mutators
	
	// Accessors
	bool GetVillagerSaved(unsigned int stage, unsigned int number);
	int GetCurrentProfile();
	unsigned int GetStageCompleted();
	bool GetCheckpoint();
	int GetPlayerLives();
	unsigned int GetCurrentLevel();
	std::wstring GetFilePath();
	Player* GetPlayer();
	EntityManager& GetEntityManager();
	TileManager* GetTileManager();
	AnimationManager* GetAnimationManager();

	char* GetCurrentObjects();
	void SetCurrentObjects(char* objects);

	// Functions other classes can use instead of having every player / enemy store audio
	void PlayEnemyAttackAudio();
	void PlayEnemyHurtAudio();
	void PlayPlayerAttackAudio();
	void PlayPlayerHurtAudio();
	void PlayPlayerJumpAudio();
	void PlayPickupAudio();
	void PlaySwitchAudio();
	void PlayCheckPointAudio();
	void PlaySpringboardAudio();

	// Set Background Music
	void SetBGM(char* song);
	void PlayBGM(bool play = true);

	// Mutators
	void SetPlayer(Player* player);
	void SetCurrentProfile(int profile);
	void SetVillagerSaved(unsigned int stage, unsigned int number, bool saved);
	void SetStageCompleted(unsigned int stage);
	void SetCheckpoint(bool checkpoint);
	void SetPlayerLives(unsigned int lives);
	void SetCurrentLevel(unsigned int level);
	void SaveGame();

	/*******************************************/
	// Accessors:
	static Game* GetInstance();
	static void	DeleteInstance();

	/*******************************************/
	// Setup, Play, Cleanup:
	bool Initialize(float width, float height);
	int	 Update();
	void Terminate();
	
	/*******************************************/
	// Screen Size Accessors:
	float GetScreenWidth() const {	return fScreenWidth;	}
	float GetScreenHeight() const {	return fScreenHeight;	} 

	/*******************************************/
	// Options Accessors:
	char GetMusicVolume() const { return cMusicVolume; }
	char GetSfxVolume() const { return cSfxVolume; }
	bool GetFullScreen() const { return bFullScreen; }

	/*******************************************/
	// Options Mutators:
	void SetMusicVolume(int);
	void SetSfxVolume(int);
	void SetFullScreen(bool);

	/*******************************************/
	// Time Attack:
	float GetTimeAttackTime();
	void PauseTimeAttack(bool pause = true);
	void SetTimeAttackTime(float time);
	void ResetTimeAttackTime();
	bool IsTimeAttackPaused();
	int GetTimeAttackLevel();
	void SetTimeAttackLevel(int level);


	/*******************************************/
	// Time Attack
	float fTime;
	float GetTime() { return fTime; }
	void SetTime(float time) { fTime = time; }
	void ResetTime() { fTime = 60.0f; }

	/*******************************************/
	// AI State Manager
	AIManagerState* GetAIManager();

	/*******************************************/
	// Bitmap Font
	BitmapFont* GetBitmapFont();

	/*******************************************/
	// Image Handles
	// Game Object Assets
	SGD::HTexture hTeamLogoImage = SGD::INVALID_HANDLE;
	SGD::HTexture hFantasyFallLogoImage = SGD::INVALID_HANDLE;
	SGD::HTexture hPauseBackgroundImage = SGD::INVALID_HANDLE;
	SGD::HTexture hOptionsImage = SGD::INVALID_HANDLE;

	SGD::HTexture hPauseBackground = SGD::INVALID_HANDLE;
	SGD::HTexture hBackground = SGD::INVALID_HANDLE;
	SGD::HTexture hProjectileImage = SGD::INVALID_HANDLE;
	SGD::HTexture hDestructableImage = SGD::INVALID_HANDLE;
	SGD::HTexture hPlayerImage = SGD::INVALID_HANDLE;
	SGD::HTexture hEnemyImage = SGD::INVALID_HANDLE;
	SGD::HTexture hBossImage = SGD::INVALID_HANDLE;
	SGD::HTexture hHPBoostImage = SGD::INVALID_HANDLE;
	SGD::HTexture hSpeedBoostImage = SGD::INVALID_HANDLE;
	SGD::HTexture hDamageBoostImage = SGD::INVALID_HANDLE;
	SGD::HTexture hCheckPointImage = SGD::INVALID_HANDLE;
	SGD::HTexture hCrumblingPlatformImage = SGD::INVALID_HANDLE;
	SGD::HTexture hSwitchImage = SGD::INVALID_HANDLE;
	SGD::HTexture hWallTriggerImage = SGD::INVALID_HANDLE;
	SGD::HTexture hSpikeImage = SGD::INVALID_HANDLE;
	SGD::HTexture hSpringboardImage = SGD::INVALID_HANDLE;
	SGD::HTexture hPapaBearImage = SGD::INVALID_HANDLE;
	SGD::HTexture hLockJawImage = SGD::INVALID_HANDLE;
	SGD::HTexture hQudzuImage = SGD::INVALID_HANDLE;
	SGD::HTexture hLavaImage = SGD::INVALID_HANDLE;
	SGD::HTexture hWaterImage = SGD::INVALID_HANDLE;
	SGD::HTexture hQuicksandImage = SGD::INVALID_HANDLE;
	SGD::HTexture hQuicksandSurfaceImage = SGD::INVALID_HANDLE;

	/*******************************************/
	// Fatory Methods
	void CreateEnemy(SGD::Point pos, int id);
	void CreatePlayer(SGD::Point pos);
	void CreateHPPowerUp(SGD::Point pos, int id);
	void CreateDamagePowerUp(SGD::Point pos, int id);
	void CreateSpeedPowerUp(SGD::Point pos, int id);
	void CreateCheckPoint(SGD::Point pos, int id);
	void CreateCrumblingPlatform(SGD::Point pos, int id);
	WallTriggerObject* CreateWallTriggerObject(SGD::Point pos, int id, unsigned int ownerID);
	void CreateSpike(SGD::Point pos, int id);
	void CreateSpringboard(SGD::Point pos, int id);
	void CreateSwitchTrigger(SGD::Point pos, int id, unsigned int targetID);
	void CreateProjectile(SGD::Point pos, SGD::Vector vec, bool facingLeft);
	void CreateEventt(SGD::Point pos);
	void CreateLiquid(SGD::Point pos, int damage, int level);
	void CreateSurfaceLiquid(SGD::Point pos, int damage, int level);
	void CreateBoss(SGD::Point pos, int id, int level);

	/*******************************************/
	// Wall Trigger Objects
	std::vector<WallTriggerObject*> GetWallTriggerObjectsVector();
	void SetWallTriggerObjectsVector(std::vector<WallTriggerObject*> wallObjects);

	/*******************************************/
	// Game State Machine:
	void ChangeState(IGameState* pNewState);
	IGameState* GetNextState() const				{ return pNextState; }

	void LoadAnimations();

	SGD::Point GetCheckpointLoc();
	int GetBottomOfLevel();
	void SetBottomOfLevel(int bottom);

	bool GetInput();
	void SetInput(bool input);

	int GetHighScore(int index);
	void AddHighScore(float score);

	void LoadHighScores();

	void SaveHighScores();

	bool HasBeatTimeAttack();
	void SetBeatTimeAttack(bool win);
};
